package com.ssy.lingxi.member.merchant.serviceimpl.base;

import com.ssy.lingxi.common.constant.basic.PurchaseContractPayTypeEnum;
import com.ssy.lingxi.common.constant.member.BusinessCategoryInvoiceTypeEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.member.merchant.api.model.vo.inner.response.BusinessCategoryFeignVO;
import com.ssy.lingxi.member.merchant.api.model.vo.inner.response.MemberCategoryFeignVO;
import com.ssy.lingxi.member.merchant.config.ServiceConfig;
import com.ssy.lingxi.member.merchant.entity.MemberBusinessCategoryDO;
import com.ssy.lingxi.member.merchant.entity.MemberClassificationDO;
import com.ssy.lingxi.member.merchant.entity.MemberRelationDO;
import com.ssy.lingxi.member.merchant.model.bo.BusinessCategoryBO;
import com.ssy.lingxi.member.merchant.model.constant.*;
import com.ssy.lingxi.member.merchant.model.dto.CountryAreaDTO;
import com.ssy.lingxi.member.merchant.model.vo.basic.request.AreaCodeVO;
import com.ssy.lingxi.member.merchant.model.vo.validate.request.BusinessCategoryDetailVO;
import com.ssy.lingxi.member.merchant.model.vo.validate.request.BusinessCategoryVO;
import com.ssy.lingxi.member.merchant.model.vo.validate.request.MemberToClassifyDepositVO;
import com.ssy.lingxi.member.merchant.model.vo.validate.response.BusinessCategoryDetailQueryVO;
import com.ssy.lingxi.member.merchant.model.vo.validate.response.BusinessCategoryQueryVO;
import com.ssy.lingxi.member.merchant.model.vo.validate.response.MemberClassifyQueryVO;
import com.ssy.lingxi.member.merchant.model.vo.validate.response.MemberClassifyVO;
import com.ssy.lingxi.member.merchant.repository.CountryAreaRepository;
import com.ssy.lingxi.member.merchant.repository.MemberBusinessCategoryRepository;
import com.ssy.lingxi.member.merchant.repository.MemberClassificationRepository;
import com.ssy.lingxi.member.merchant.service.base.IBaseMemberClassificationService;
import com.ssy.lingxi.member.merchant.utils.AgentAreaUtil;
import com.ssy.lingxi.member.merchant.utils.NumberUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author 万宁
 * @version 2.0.0
 * @date 2021-05-31
 */
@Service
public class BaseMemberClassificationServiceImpl implements IBaseMemberClassificationService {
    @Resource
    private MemberClassificationRepository memberClassificationRepository;

    @Resource
    private MemberBusinessCategoryRepository memberBusinessCategoryRepository;

    @Resource
    private CountryAreaRepository countryAreaRepository;

    /**
     * 保存会员入库信息，外部调用方要保存MemberRelationDO
     *
     * @param relationDO      会员关系
     * @param code            会员编码
     * @param partnerType 会员合作类型枚举
     * @param maxAmount       合作金额
     * @param areaCodes       适用区域
     * @param categories      主营品类列表
     * @return 保存结果
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Wrapper<Void> saveMemberClassification(MemberRelationDO relationDO, String remark, Integer currencyType, String code, Integer partnerType, BigDecimal maxAmount, List<AreaCodeVO> areaCodes, List<BusinessCategoryVO> categories) {
        //判断会员编码是否重复
        List<MemberClassificationDO> classificationList = memberClassificationRepository.findByMemberIdAndRoleId(relationDO.getMemberId(), relationDO.getRoleId());
        if(!CollectionUtils.isEmpty(classificationList) && relationDO.getClassification() == null && classificationList.stream().anyMatch(clf -> clf.getCode().toLowerCase().equals(code.toLowerCase()))) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_CLASSIFY_CODE_EXISTS);
        }

        if(!CollectionUtils.isEmpty(classificationList) && relationDO.getClassification() != null && classificationList.stream().filter(cf -> !cf.getRelation().getId().equals(relationDO.getId())).anyMatch(clf -> clf.getCode().toLowerCase().equals(code.toLowerCase()))) {
            return Wrapper.fail(ResponseCode.MC_MS_MEMBER_CLASSIFY_CODE_EXISTS);
        }

        //一级品类不能重复
        if(categories.stream().flatMap(c -> c.getDetails().stream().filter(d -> d.getLevel().equals(1)).map(BusinessCategoryDetailVO::getCategoryId)).distinct().count() != categories.size()) {
            return Wrapper.fail(ResponseCode.MC_MS_BUSINESS_LEVEL_ONE_CATEGORY_DUPLICATED);
        }

        //校验主营品类信息中的结算周期
        for (BusinessCategoryVO category : categories) {
            //品类层级要从1开始
            if(category.getDetails().stream().min(Comparator.comparingInt(BusinessCategoryDetailVO::getLevel)).map(BusinessCategoryDetailVO::getLevel).orElse(0) != 1) {
                return Wrapper.fail(ResponseCode.MC_MS_BUSINESS_CATEGORY_LEVEL_MUST_START_WITH_ONE);
            }

            //品类层级不能重复
            if(category.getDetails().stream().map(BusinessCategoryDetailVO::getLevel).distinct().count() != category.getDetails().size()) {
                return Wrapper.fail(ResponseCode.MC_MS_BUSINESS_CATEGORY_LEVEL_MUST_START_WITH_ONE);
            }

            //品类层级要每次加1
            if(category.getDetails().stream().max(Comparator.comparingInt(BusinessCategoryDetailVO::getLevel)).map(BusinessCategoryDetailVO::getLevel).orElse(0) != category.getDetails().size()) {
                return Wrapper.fail(ResponseCode.MC_MS_BUSINESS_CATEGORY_LEVEL_MUST_START_WITH_ONE);
            }

            //账期（按月）
            if(category.getPayType().equals(PurchaseContractPayTypeEnum.PERIOD_MONTHLY.getCode())) {
                if(NumberUtil.isNullOrNegative(category.getMonth())) {
                    return Wrapper.fail(ResponseCode.MC_MS_BUSINESS_CATEGORY_MONTH_MUST_GT_ZERO);
                }

                if(NumberUtil.isNullOrNegative(category.getMonthDay())) {
                    return Wrapper.fail(ResponseCode.MC_MS_BUSINESS_CATEGORY_MONTH_DAY_MUST_GT_ZERO);
                }
            }

            //账期（按天）
            if(category.getPayType().equals(PurchaseContractPayTypeEnum.PERIOD_DAILY.getCode())) {
                if(NumberUtil.isNullOrNegative(category.getDays())) {
                    return Wrapper.fail(ResponseCode.MC_MS_BUSINESS_CATEGORY_MONTH_DAY_MUST_GT_ZERO);
                }
            }

            //月结
            if(category.getPayType().equals(PurchaseContractPayTypeEnum.MONTHLY.getCode())) {
                if(NumberUtil.isNullOrNegative(category.getMonthDay())) {
                    return Wrapper.fail(ResponseCode.MC_MS_BUSINESS_CATEGORY_MONTH_DAY_MUST_GT_ZERO);
                }
            }
        }

        //删除旧数据
        MemberClassificationDO classification = relationDO.getClassification();
        if(classification != null) {
            relationDO.setClassification(null);
            memberClassificationRepository.delete(classification);
        }

        List<MemberBusinessCategoryDO> categoryList = categories.stream().map(businessCategoryVO -> {
            MemberBusinessCategoryDO categoryDO = new MemberBusinessCategoryDO();
            categoryDO.setCategories(businessCategoryVO.getDetails().stream().map(detail -> new BusinessCategoryBO(detail.getLevel(), detail.getCategoryId(), detail.getName())).collect(Collectors.toList()));
            categoryDO.setPayType(businessCategoryVO.getPayType());
            categoryDO.setMonth(NumberUtil.isNullOrZero(businessCategoryVO.getMonth()) ? 0 : businessCategoryVO.getMonth());
            categoryDO.setMonthDay(NumberUtil.isNullOrZero(businessCategoryVO.getMonthDay()) ? 0 : businessCategoryVO.getMonthDay());
            categoryDO.setDays(NumberUtil.isNullOrZero(businessCategoryVO.getDays()) ? 0 : businessCategoryVO.getDays());
            categoryDO.setInvoiceType(businessCategoryVO.getInvoiceType());
            categoryDO.setInvoiceTypeName(BusinessCategoryInvoiceTypeEnum.getNameByCode(businessCategoryVO.getInvoiceType()));
            categoryDO.setTaxPoint(businessCategoryVO.getTaxPoint().divide(BigDecimal.valueOf(100), 2 , RoundingMode.HALF_UP));
            categoryDO.setAdvanceCharge(businessCategoryVO.getAdvanceCharge());
            categoryDO.setSettlementDocuments(businessCategoryVO.getSettlementDocuments());
            categoryDO.setPaymentType(businessCategoryVO.getPaymentType());
            return categoryDO;
        }).collect(Collectors.toList());
        memberBusinessCategoryRepository.saveAll(categoryList);

        classification = new MemberClassificationDO();
        classification.setMemberId(relationDO.getMemberId());
        classification.setRoleId(relationDO.getRoleId());
        classification.setCode(code);
        classification.setRelation(relationDO);
        classification.setPartnerType(partnerType);
        classification.setMaxAmount(maxAmount);
        classification.setRemark(remark);
        classification.setCurrencyType(currencyType);
        if(CollectionUtils.isEmpty(areaCodes)) {
            classification.setAreas(new ArrayList<>());
        } else {
            List<String> codeList = areaCodes.stream().flatMap(areaCodeVO -> {
                if(StringUtils.hasLength(areaCodeVO.getCityCode())) {
                    return Stream.of(areaCodeVO.getProvinceCode(), areaCodeVO.getCityCode());
                } else {
                    return Stream.of(areaCodeVO.getProvinceCode());
                }
            }).distinct().collect(Collectors.toList());

            List<CountryAreaDTO> dtoList = countryAreaRepository.findByCodeIn(codeList, CountryAreaDTO.class);
            if(codeList.size() != dtoList.size()) {
                return Wrapper.fail(ResponseCode.MC_MS_COUNTRY_AREA_DOES_NOT_EXIST);
            }

            classification.setAreas(AgentAreaUtil.transferToBO(areaCodes, dtoList));
        }
        classification.setCategories(new HashSet<>(categoryList));
        memberClassificationRepository.saveAndFlush(classification);

        //在这里设置MemberRelation的关联，外部调用方要保存MemberRelation
        relationDO.setClassification(classification);

        return Wrapper.success();
    }

    /**
     * 查询会员入库分类信息
     *
     * @param relationDO 会员关系
     * @return 查询结果
     */
    @Override
    public MemberClassifyVO getMemberClassification(MemberRelationDO relationDO) {
        MemberClassifyVO classifyVO = new MemberClassifyVO();
        MemberClassificationDO classification = relationDO.getClassification();
        if(classification != null) {
            classifyVO.setCode(classification.getCode());
            classifyVO.setPartnerTypeName(MemberPartnerTypeEnum.getCodeMsg(classification.getPartnerType()));
            classifyVO.setCurrencyType(classification.getCurrencyType());
            classifyVO.setCurrencyTypeName(CurrencyTypeEnum.getCodeMsg(classification.getCurrencyType()));
            classifyVO.setRemark(classification.getRemark());
            //保留两位小数
            DecimalFormat df = new DecimalFormat("#0.00");
            classifyVO.setMaxAmount(df.format(classification.getMaxAmount()));
            classifyVO.setClassifyAreas(AgentAreaUtil.transferToList(classification.getAreas()));
            if(!CollectionUtils.isEmpty(classification.getCategories())) {
                classifyVO.setCategories(classification.getCategories().stream().sorted(Comparator.comparingLong(MemberBusinessCategoryDO::getId)).map(memberBusinessCategoryDO -> {
                    BusinessCategoryQueryVO queryVO = new BusinessCategoryQueryVO();
                    queryVO.setId(memberBusinessCategoryDO.getId());
                    queryVO.setDetails(CollectionUtils.isEmpty(memberBusinessCategoryDO.getCategories()) ? new ArrayList<>() : memberBusinessCategoryDO.getCategories().stream().map(category -> new BusinessCategoryDetailQueryVO(category.getLevel(), category.getCategoryId(), category.getName())).sorted(Comparator.comparingInt(BusinessCategoryDetailQueryVO::getLevel)).collect(Collectors.toList()));
                    queryVO.setPayType(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getPayType()) ? 0 : memberBusinessCategoryDO.getPayType());
                    queryVO.setPayTypeName(PurchaseContractPayTypeEnum.getNameByCode(queryVO.getPayType()));
                    queryVO.setMonth(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getMonth()) ? 0 : memberBusinessCategoryDO.getMonth());
                    queryVO.setMonthDay(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getMonthDay()) ? 0 : memberBusinessCategoryDO.getMonthDay());
                    queryVO.setDays(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getDays()) ? 0 : memberBusinessCategoryDO.getDays());
                    queryVO.setInvoiceType(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getInvoiceType()) ? 0 : memberBusinessCategoryDO.getInvoiceType());
                    queryVO.setInvoiceTypeName(BusinessCategoryInvoiceTypeEnum.getNameByCode(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getInvoiceType()) ? 0 : memberBusinessCategoryDO.getInvoiceType()));
                    queryVO.setTaxPoint(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getTaxPoint()) ? "0" : ServiceConfig.BIG_DECIMAL_FORMAT.format(memberBusinessCategoryDO.getTaxPoint().multiply(BigDecimal.valueOf(100))));
                    queryVO.setAdvanceCharge(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getInvoiceType()) ? 0 : memberBusinessCategoryDO.getAdvanceCharge());
                    queryVO.setAdvanceChargeName(AdvanceChargeEnum.getNameByCode(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getAdvanceCharge()) ? 0 : memberBusinessCategoryDO.getAdvanceCharge()));
                    queryVO.setSettlementDocuments(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getSettlementDocuments()) ? 0 : memberBusinessCategoryDO.getSettlementDocuments());
                    queryVO.setSettlementDocumentsName(SettlementDocumentsEnum.getNameByCode(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getSettlementDocuments()) ? 0 : memberBusinessCategoryDO.getSettlementDocuments()));
                    queryVO.setPaymentType(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getPaymentType()) ? 0 : memberBusinessCategoryDO.getPaymentType());
                    queryVO.setPaymentTypeName(PaymentTypeEnum.getNameByCode(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getPaymentType()) ? 0 : memberBusinessCategoryDO.getPaymentType()));
                    return queryVO;
                }).collect(Collectors.toList()));
            }
        }
        return classifyVO;
    }

    /**
     * 查询会员入库分类信息（可编辑）
     *
     * @param relationDO 会员关系
     * @return 查询结果
     */
    @Override
    public MemberClassifyQueryVO findMemberClassification(MemberRelationDO relationDO) {
        MemberClassifyQueryVO classifyVO = new MemberClassifyQueryVO();
        classifyVO.setPartnerTypes(MemberPartnerTypeEnum.toDropdownList());

        MemberClassificationDO classification = relationDO.getClassification();
        if(classification != null) {
            classifyVO.setCode(classification.getCode());
            classifyVO.setPartnerType(classification.getPartnerType());
            classifyVO.setPartnerTypes(MemberPartnerTypeEnum.toEditableDropdownList());
            classifyVO.setCurrencyType(classification.getCurrencyType());
            classifyVO.setCurrencyTypeName(CurrencyTypeEnum.getCodeMsg(classification.getCurrencyType()));
            classifyVO.setRemark(classification.getRemark());
            //保留两位小数
            DecimalFormat df = new DecimalFormat("#0.00");
            classifyVO.setMaxAmount(df.format(classification.getMaxAmount()));
            classifyVO.setAreaCodes(AgentAreaUtil.transferToCodeList(classification.getAreas()));
            if(!CollectionUtils.isEmpty(classification.getCategories())) {
                classifyVO.setCategories(classification.getCategories().stream().sorted(Comparator.comparingLong(MemberBusinessCategoryDO::getId)).map(memberBusinessCategoryDO -> {
                    BusinessCategoryQueryVO queryVO = new BusinessCategoryQueryVO();
                    queryVO.setId(memberBusinessCategoryDO.getId());
                    queryVO.setDetails(memberBusinessCategoryDO.getCategories().stream().map(category -> new BusinessCategoryDetailQueryVO(category.getLevel(), category.getCategoryId(), category.getName())).sorted(Comparator.comparingInt(BusinessCategoryDetailQueryVO::getLevel)).collect(Collectors.toList()));
                    queryVO.setPayType(memberBusinessCategoryDO.getPayType());
                    queryVO.setPayTypeName(PurchaseContractPayTypeEnum.getNameByCode(memberBusinessCategoryDO.getPayType()));
                    queryVO.setMonth(memberBusinessCategoryDO.getMonth());
                    queryVO.setMonthDay(memberBusinessCategoryDO.getMonthDay());
                    queryVO.setDays(memberBusinessCategoryDO.getDays());
                    queryVO.setInvoiceType(memberBusinessCategoryDO.getInvoiceType());
                    queryVO.setInvoiceTypeName(BusinessCategoryInvoiceTypeEnum.getNameByCode(memberBusinessCategoryDO.getInvoiceType()));
                    queryVO.setTaxPoint(ServiceConfig.BIG_DECIMAL_FORMAT.format(memberBusinessCategoryDO.getTaxPoint()));
                    queryVO.setAdvanceCharge(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getInvoiceType()) ? 0 : memberBusinessCategoryDO.getAdvanceCharge());
                    queryVO.setAdvanceChargeName(AdvanceChargeEnum.getNameByCode(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getAdvanceCharge()) ? 0 : memberBusinessCategoryDO.getAdvanceCharge()));
                    queryVO.setSettlementDocuments(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getSettlementDocuments()) ? 0 : memberBusinessCategoryDO.getSettlementDocuments());
                    queryVO.setSettlementDocumentsName(SettlementDocumentsEnum.getNameByCode(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getSettlementDocuments()) ? 0 : memberBusinessCategoryDO.getSettlementDocuments()));
                    queryVO.setPaymentType(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getPaymentType()) ? 0 : memberBusinessCategoryDO.getPaymentType());
                    queryVO.setPaymentTypeName(PaymentTypeEnum.getNameByCode(NumberUtil.isNullOrZero(memberBusinessCategoryDO.getPaymentType()) ? 0 : memberBusinessCategoryDO.getPaymentType()));
                    return queryVO;
                }).collect(Collectors.toList()));
            }
        }
        return classifyVO;
    }

    /**
     * 查询会员入库分类信息 - 主营品类信息
     *
     * @param relationDO 会员关系
     * @return 查询结果
     */
    @Override
    public List<MemberCategoryFeignVO> findMemberBusinessCategories(MemberRelationDO relationDO) {
        MemberClassificationDO classification = relationDO.getClassification();
        if(classification == null || CollectionUtils.isEmpty(classification.getCategories())) {
            return new ArrayList<>();
        }

        return classification.getCategories().stream().map(memberBusinessCategoryDO -> {
            MemberCategoryFeignVO queryVO = new MemberCategoryFeignVO();
            queryVO.setCategories(memberBusinessCategoryDO.getCategories().stream().map(category -> {
                BusinessCategoryFeignVO feignVO = new BusinessCategoryFeignVO();
                feignVO.setLevel(category.getLevel());
                feignVO.setCategoryId(category.getCategoryId());
                feignVO.setName(category.getName());
                return feignVO;
            }).sorted(Comparator.comparingInt(BusinessCategoryFeignVO::getLevel)).collect(Collectors.toList()));
            queryVO.setPayType(memberBusinessCategoryDO.getPayType());
            queryVO.setPayTypeName(PurchaseContractPayTypeEnum.getNameByCode(memberBusinessCategoryDO.getPayType()));
            queryVO.setMonth(memberBusinessCategoryDO.getMonth());
            queryVO.setMonthDay(memberBusinessCategoryDO.getMonthDay());
            queryVO.setDays(memberBusinessCategoryDO.getDays());
            queryVO.setInvoiceType(memberBusinessCategoryDO.getInvoiceType());
            queryVO.setInvoiceTypeName(memberBusinessCategoryDO.getInvoiceTypeName());
            queryVO.setTaxPoint(memberBusinessCategoryDO.getTaxPoint());
            return queryVO;
        }).collect(Collectors.toList());
    }
}
